//////////////////////////////////////////////////////////////////////////////
//
// Socket input stream to convert byte stream from network to host
//
//////////////////////////////////////////////////////////////////////////////
#ifndef __SERVER_HELPER_HPP
#define __SERVER_HELPER_HPP

#include "opapi.h"
#include "record_helper.hpp"
#include <magus/network/Socket.hpp>

//////////////////////////////////////////////////////////////////////////////

namespace server_helper
{

// only for read and write
#define S_ASSERT(T) { int (rv) = (T); if( (rv) < 0 ) return (rv); }

// read update/insert history value
inline int readValue(Socket &sock, PointValue *p)
{
	S_ASSERT(sock.readInt((int *)&p->tm_tag));
	S_ASSERT(sock.readShort(&p->status));
	S_ASSERT(sock.readInt(&p->xv.lval));
	return 0;
}

// Point Value
inline int read(Socket &sock, PointValue *p, int with_time)
{
	memset(p, 0, sizeof(PointValue));
	S_ASSERT(sock.readByte(&p->type));
	switch(p->type)
	{
	case AX_TYPE:
		if (with_time != 0)
			S_ASSERT(sock.readInt((int *)&p->tm_tag));
		S_ASSERT(sock.readShort(&p->status));
		S_ASSERT(sock.readFloat(&p->xv.fval));
		break;
	case DX_TYPE:
		if (with_time != 0)
			S_ASSERT(sock.readInt((int *)&p->tm_tag));
		S_ASSERT(sock.readShort(&p->status));
		p->xv.bval = p->status & 0x1;
		break;
	case I2_TYPE:
		if (with_time != 0)
			S_ASSERT(sock.readInt((int *)&p->tm_tag));
		S_ASSERT(sock.readShort(&p->status));
		S_ASSERT(sock.readShort(&p->xv.ival));
		break;
	case I4_TYPE:
		if (with_time != 0)
			S_ASSERT(sock.readInt((int *)&p->tm_tag));
		S_ASSERT(sock.readShort(&p->status));
		S_ASSERT(sock.readInt(&p->xv.lval));
		break;
	}
	return 0;
}

inline int write(Socket &sock, const PointValue &val, int with_time)
{
	int rv = sock.writeByte(val.type);
	switch(val.type)
	{
	case AX_TYPE:
		if (with_time != 0)
			rv+= sock.writeInt(val.tm_tag);
		rv+= sock.writeShort(val.status);
		rv+= sock.writeFloat(val.xv.fval);
		break;
	case DX_TYPE:
		if (with_time != 0)
			rv+= sock.writeInt(val.tm_tag);
		rv+= sock.writeShort(val.status);
		break;
	case I2_TYPE:
		if (with_time != 0)
			rv+= sock.writeInt(val.tm_tag);
		rv+= sock.writeShort(val.status);
		rv+= sock.writeShort(val.xv.ival);
		break;
	case I4_TYPE:
		if (with_time != 0)
			rv+= sock.writeInt(val.tm_tag);
		rv+= sock.writeShort(val.status);
		rv+= sock.writeInt(val.xv.lval);
		break;
	}
	return rv;
}

// RootInfo
inline int read(Socket& sock, RootInfo* pri)
{   
    int len;
	S_ASSERT(sock.readInt((long *)&pri->id));  
	S_ASSERT(sock.readUTF((char *)&pri->name, OP_NAME_LEN, &len));
	pri->name[OP_NAME_LEN] = 0;
	S_ASSERT(sock.readUTF((char *)&pri->desc, OP_DESC_LEN, &len));   
	pri->desc[OP_DESC_LEN] = 0;
	S_ASSERT(sock.readInt(&pri->unit_limit));   
	S_ASSERT(sock.readInt(&pri->node_limit));   
	S_ASSERT(sock.readInt(&pri->point_limit));   
	return 0;
}

inline int write(Socket& sock, const RootInfo& ri)
{
	S_ASSERT(sock.writeInt(ri.id));  
	S_ASSERT(sock.writeUTF((char *)&ri.name, OP_NAME_LEN));
	S_ASSERT(sock.writeUTF((char *)&ri.desc, OP_DESC_LEN));   
	S_ASSERT(sock.writeInt(ri.unit_limit));  
	S_ASSERT(sock.writeInt(ri.node_limit));  
	S_ASSERT(sock.writeInt(ri.point_limit));  
	return 0;
}

// UnitInfo
inline int read(Socket& sock, UnitInfo* pui)
{   
    int len;
	S_ASSERT(sock.readInt((long *)&pui->id));  
	if (pui->id < 0)
	    return 0;
	S_ASSERT(sock.readUTF((char *)&pui->name, OP_NAME_LEN, &len));
	pui->name[OP_NAME_LEN] = 0;
	S_ASSERT(sock.readUTF((char *)&pui->desc, OP_DESC_LEN, &len));   
	pui->desc[OP_DESC_LEN] = 0;
	return 0;
}

inline int write(Socket& sock, const UnitInfo& ui)
{
	S_ASSERT(sock.writeInt(ui.id));  
	S_ASSERT(sock.writeUTF((char *)&ui.name, OP_NAME_LEN));
	S_ASSERT(sock.writeUTF((char *)&ui.desc, OP_DESC_LEN));   
	return 0;
}
     
// NodeInfo
inline int read(Socket& sock, NodeInfo* pni)
{
    int len;
	S_ASSERT(sock.readInt((long *)&pni->id));
	if (pni->id < 0)
	    return 0;
	S_ASSERT(sock.readInt((long *)&pni->parent_id));  
	S_ASSERT(sock.readInt((long *)&pni->type));  
	S_ASSERT(sock.readUTF((char *)&pni->name, OP_NAME_LEN, &len));
	pni->name[OP_NAME_LEN] = 0;
	S_ASSERT(sock.readUTF((char *)&pni->desc, OP_DESC_LEN, &len)); 
	pni->desc[OP_DESC_LEN] = 0;
	S_ASSERT(sock.readInt((long *)&pni->freq));  
	S_ASSERT(sock.readInt((long *)&pni->compress));  
	S_ASSERT(sock.readInt(&pni->access_time));  
	return 0;
}

inline int write(Socket& sock, const NodeInfo& ni)
{
	S_ASSERT(sock.writeInt(ni.id));
	S_ASSERT(sock.writeInt(ni.parent_id));  
	S_ASSERT(sock.writeInt(ni.type));  
	S_ASSERT(sock.writeUTF((char *)&ni.name, OP_NAME_LEN));
	S_ASSERT(sock.writeUTF((char *)&ni.desc, OP_DESC_LEN));    
	S_ASSERT(sock.writeInt(ni.freq));
	S_ASSERT(sock.writeInt(ni.compress));
	S_ASSERT(sock.writeInt(ni.access_time));
	return 0;
}
  
// PointInfo
inline int read(Socket& sock, PointInfo* ppi)
{
    char buf[OP_PI_LEN + 2], expr[OP_EX_LEN];
    int len = OP_PI_LEN - 4 + 2;
    
	int rv = sock.readInt(&ppi->id);  
	if (rv != 0 || ppi->id < 0) 
	    return rv;
    
    rv = sock.readBytes(buf+4, len);
    if (rv == 0) {
        pointGet(buf, ppi, true);
        memset(expr, 0, sizeof(expr));
        len = ushortGet(buf, OP_PI_LEN);
        if (len > OP_EX_LEN-1)
        	return -1;
        rv = sock.readBytes(expr, len);
        if (ppi->point_type == CALC_POINT && rv == 0 && len > 0)
        {
            ppi->expression = (char *)malloc(OP_EX_LEN);
            if (ppi->expression)
                memcpy(ppi->expression, expr, OP_EX_LEN);
        }
    }
    return rv;
}

inline int write(Socket& sock, const PointInfo& pi)
{
    int rv, len;
    char buf[OP_PI_LEN + 2];
    
    pointStore(buf, pi);
    if (pi.point_type != CALC_POINT || pi.expression == NULL)
    {
    	shortStore(buf + OP_PI_LEN, 0);			// info.expression = NULL;
        rv = sock.Send(buf, OP_PI_LEN + 2);
    }else{
        len = strlen(pi.expression);
        rv = sock.Send(buf, OP_PI_LEN);
        rv+= sock.writeUTF(pi.expression, len);
    }
    return rv;
}

} 	// class server_helper

//////////////////////////////////////////////////////////////////////////////

#endif //#ifndef __SERVER_HELPER_HPP 

